use std::io::{Result,ErrorKind, Error};
use std::any::TypeId;
use std::collections::HashMap;
use std::intrinsics::type_id;

use super::layout;
use super::addons::CPBuffer;
use super::types::{meta::Meta,mbox::MBox,ukey::Ukey};

#[repr(C)]
#[derive(Debug,Default,Clone,Copy,PartialEq,Hash)]
pub struct TypeData {
    t : u64,
    s : usize,
}
impl TypeData {
    const Aligned:usize = 0x20;
    fn aligne(l: usize) -> usize {
        let e = l+Self::Aligned-1usize;
        let b = e/Self::Aligned;
        b*Self::Aligned
    }
    pub fn from<T:'static>() -> Self {
        Self { 
            t:type_id::<T>(),
            s:Self::aligne(std::mem::size_of::<T>())
        }
    }
}
#[repr(C)]
#[derive(Debug,Default,Clone,Copy,PartialEq)]
pub struct DescItem {
    key : Ukey,
    t : TypeData,
}

#[derive(Debug,Default,Clone,Hash)]
pub struct NameItem {
    n : String,
    t : TypeData,
}
impl NameItem {
    pub fn from<T:'static>(n:&str) -> Self {
        Self { 
            n:n.to_string(),
            t:TypeData::from::<T>()
        }
    }
}
impl DescItem {
    pub fn new(b: &NameItem) -> Self {
        Self { 
            key:Ukey::of::<NameItem>(b),
            t:b.t
        }
    }
    pub fn check(&self,b: &NameItem) -> bool {
        Self::new(b) == *self
    }
}
#[derive(Debug)]
pub struct BlockDesc {
    pub record:[DescItem;layout::SYS_BLOCK_DESC_LEN],
    pub size: usize,
}

impl BlockDesc {
    pub fn attach(m: &Meta) -> Result<MBox<BlockDesc>> {
        assert!(std::mem::size_of::<BlockDesc>()<0x1000usize-0x200usize);
        m.attach::<BlockDesc>(0x200)
    }
    pub fn dump(&self) {
        println!("desc size {}",self.size);
    }
}

pub struct SysBlocks {
    blocks: Vec<NameItem>,
    cpb : CPBuffer,
    bname: HashMap<String,(DescItem,usize)>,
    desc : MBox<BlockDesc>
}

impl SysBlocks {    
    fn build_map(&mut self,blocks:&Vec<NameItem>) -> Result<()> {
        self.bname.clear();
        let mut offset = 0usize;
        for i in 0..blocks.len() {
            if self.bname.contains_key(&blocks[i].n) {
                return Err(Error::new(ErrorKind::Other,"dup block"))
            }
            self.bname.insert(blocks[i].n.clone(), (DescItem::new(&blocks[i]),offset));
            offset += self.desc.record[i].t.s;
            self.blocks.push(blocks[i].clone());
        }
        Ok(())
    }
    pub fn check(&mut self,blocks:&Vec<NameItem>) -> Result<()> {
        if self.desc.size != blocks.len() {
            return Err(Error::new(ErrorKind::Other,"diff desc len"));
        }
        if self.desc.size > layout::SYS_BLOCK_DESC_LEN {
            return Err(Error::new(ErrorKind::Other,"too many block in meta"));
        }
        if blocks.len() > layout::SYS_BLOCK_DESC_LEN {
            return Err(Error::new(ErrorKind::Other,"too many block to check"));
        }
        self.blocks.clear();
        for i in 0..blocks.len() {
            if !self.desc.record[i].check(&blocks[i]) {
                return Err(Error::new(ErrorKind::Other,"diff type"))
            }
            self.blocks.push(blocks[i].clone());
        }
        self.build_map(blocks)?;
        Ok(())
    }
    pub fn build(&mut self, blocks: &Vec<NameItem>) -> Result<()> {
        if blocks.len() > layout::SYS_BLOCK_DESC_LEN {
            return Err(Error::new(ErrorKind::Other,"too many block to build"));
        }
        self.desc.size = blocks.len();
        
        self.build_map(blocks)?;
        self.blocks.clear();
        for i in 0..blocks.len() {
            self.desc.record[i] = (*self.bname.get(&blocks[i].n).unwrap()).0;
            self.blocks.push(blocks[i].clone());
        }
        Ok(())
    }

    pub fn new(fname: String) -> Result<Self> {
        let cpb = CPBuffer::attach(fname)?;
        let desc = BlockDesc::attach(&cpb.block.meta)?;
        Ok(Self { blocks: vec![], cpb:cpb, desc: desc, bname:HashMap::new() })
    }
    
    pub fn addSome<T:'static>(&mut self,name:&String) -> Result<()>{
        let ni = NameItem::from::<T>(&name);
        let di = DescItem::new(&ni);
        let mut offset = 0usize;
        for i in 0usize..self.desc.size {
            if self.desc.record[i] == di {
                match self.bname.get(name) {
                    None => {
                        self.bname.insert(name.to_string(),(di,offset));
                    },
                    Some((tdi,off)) => {
                        if *off!=offset || *tdi != di {
                            self.bname.insert(name.to_string(),(di,offset));
                        }
                    },
                }
                return Ok(());
            }
            offset += self.desc.record[i].t.s;
        }
        Err(Error::new(ErrorKind::Other,"not find when add"))
    }

    pub fn get_blocks(&self) -> Vec<NameItem> {
        self.blocks.clone()
    }

    pub fn get_items<T:'static>(&mut self,name:&str) -> Result<MBox<T>> {
        match self.bname.get(name) {
            None => {
                let _ = self.addSome::<T>(&name.to_owned())?;
            },
            _ => {},
        }
        match self.bname.get(name) {
            None => Err(Error::new(ErrorKind::Other,"not find not possible")),
            Some((_,off)) => self.cpb.block.meta.attach::<T>(*off+0x1000usize),
        }
    }
}


